/*
 * Daniel Singleton for C++
 * Created by Denes Daniel
 *
 * This file is licensed to you under the Apache License, Version 2.0 :
 * http://www.apache.org/licenses/LICENSE-2.0
 * Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
 */
 
#ifndef STONKEEPER_H
#define STONKEEPER_H

// Singleton Keeper implementation
// Responsible for creating and destroying Singleton at the right time based on dependencies

// Include default Manager class for StonKeeper
#include "stonmanager.h"

template <class Ston, template <class> class Manager = StonManager>
class StonKeeper {
    friend class Manager<Ston>;
    typedef Manager<Ston> TManager;
    static bool initRunning;
    static bool initComplete;
    static bool doInit;
    static Ston * newSingleton();
    static void delSingleton(Ston * inst);
    StonKeeper();
    StonKeeper(StonKeeper<Ston, Manager> const &);
    StonKeeper<Ston, Manager> & operator =(StonKeeper<Ston, Manager> const &);
    ~StonKeeper();
public:
    class SingletonNotUsableException {};
    class CircularDependencyException {};
    static bool keepSingletonAlive();
    static Ston * getSingleton();
};

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::initRunning = false;

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::initComplete = false;

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::doInit = StonKeeper<Ston, Manager>::keepSingletonAlive();

template <class Ston, template <class> class Manager>
inline Ston * StonKeeper<Ston, Manager>::newSingleton() {
    return new Ston();
}

template <class Ston, template <class> class Manager>
inline void StonKeeper<Ston, Manager>::delSingleton(Ston * inst) {
    delete inst;
}

template <class Ston, template <class> class Manager>
inline StonKeeper<Ston, Manager>::StonKeeper() {
}

template <class Ston, template <class> class Manager>
StonKeeper<Ston, Manager>::~StonKeeper() {
    TManager::killSingleton();
}

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::keepSingletonAlive() {
    if(!initComplete) {
        if(initRunning) throw CircularDependencyException();
        initRunning = true;
        try {
            Ston::setupDepends();
            TManager::doInit();
        }
        catch (...) {
            initRunning = false;
            throw;
        }
        static StonKeeper<Ston, Manager> keeper;
        initRunning = false;
        initComplete = true;
    }
    return true;
}

template <class Ston, template <class> class Manager>
Ston * StonKeeper<Ston, Manager>::getSingleton() {
    Ston * inst = TManager::getSingleton();
    if(!inst) {
        if(!initComplete) {
            (void)doInit; // Needed so init is not optimized away
            throw SingletonNotUsableException();
        }
        inst = TManager::makeSingleton();
        if(!inst) throw SingletonNotUsableException();
    }
    return inst;
}

#endif
